#ifndef MESSAGE_LOGIC_H_INCLUDED
#define MESSAGE_LOGIC_H_INCLUDED

/*
* Copyright (c) 2010 Michael Collins
*
* This file is part of TerraFirma.
*
* TerraFirma is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TerraFirma is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TerraFirma.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <message_header.h>
#include <task_thread.h>
#include <player.h>
#include <entity.h>

namespace engine
{

    /** The task id code of the logic thread
    */
    static const task_id logic_thread_tid = get_task_id_by_name("logic_thread");

    //Minor-type enumeration to tell the thread how it should treat this message
    enum {PLAYER_ADD,
          PLAYER_UPDATE,
          PLAYER_REMOVE,
          ENTITY_CREATE,
          ENTITY_UPDATE,
          DATA_UPDATE,
          ENTITY_DESTROY,
          CONTROL_EVENT,
          REQUEST_JOIN,
          ACCEPT_JOIN,
          GAME_SIGNAL
         };
    //**** Player-specific messages for updating the status of the player lists on server and clients****//

    /** broad message that can be sent to the logic thread and it will parse it
     *  This is just supposed to be a dumb container that can hold the largest possible
     *  actual message with content. POD type will cast perfectly
     */
    typedef message<92> Message_Logic;

    struct Player_Connect_Message
    {
        Player_Connect_Message() : header(logic_thread_tid, GAME_MSG, PLAYER_ADD, sizeof(Player_Connect_Message)) {}
        Player_Connect_Message(const game::Player& p) :
                header(logic_thread_tid, GAME_MSG, 0, sizeof(Player_Connect_Message)),
                player(p) {}
        const Message_Logic& upcast() {return *(reinterpret_cast<const Message_Logic*>(this));}

        message_header header;
        game::Player player;
    };

    struct Player_Update_Message
    {
        Player_Update_Message() : header(logic_thread_tid, GAME_MSG, PLAYER_UPDATE, sizeof(Player_Update_Message)) {}
        Player_Update_Message(uint8_t f, const game::Player& p) :
                header(logic_thread_tid, GAME_MSG, 1, sizeof(Player_Update_Message)), field(f) {}
        const Message_Logic& upcast() {return *(reinterpret_cast<const Message_Logic*>(this));}

        Player_Update_Message(uint8_t f, const void* c) :
        header(logic_thread_tid, GAME_MSG, PLAYER_UPDATE,
        sizeof(header) + sizeof(field) +
        (f==NAME)?strlen((char*)c) :
        (f==SCORE)? sizeof(int16_t) :
        (f==TEAM)? sizeof(uint8_t) :
        (f==PING)? sizeof(uint16_t) :
        (f==VEHICLE)? sizeof(uint32_t) :
        (f==ALIVE)? sizeof(bool) :
        (f==READY)? sizeof(bool) :
        0),
        field(f)
        {
            switch(f) {
            case NAME:
                strncpy(data.name, (char*)c, 64); break;
            case SCORE:
                data.score = *(uint16_t*)c; break;
            case TEAM:
                data.team = *(uint8_t*)c; break;
            case PING:
                data.ping = *(uint16_t*)c; break;
            case VEHICLE:
                data.vehicle = *(uint32_t*)c; break;
            };
        }


        enum {NAME, SCORE, TEAM, PING, VEHICLE, ALIVE, READY}; //Enumerates the possible fields to change

        message_header header;
        uint8_t field; //Indicates which field changed
        union {
            char     name [64];
            uint8_t  team;
            uint16_t ping;
            uint16_t score;
            uint32_t vehicle;
        } data;
    };

    struct Player_Disconnect_Message
    {
        Player_Disconnect_Message() : header(logic_thread_tid, GAME_MSG, PLAYER_REMOVE, sizeof(Player_Disconnect_Message)) {}
        Player_Disconnect_Message(uint8_t pid) :
                header(logic_thread_tid, GAME_MSG, 2, sizeof(Player_Disconnect_Message)),
                player(pid) {}
        Player_Disconnect_Message(const game::Player& p) :
                header(logic_thread_tid, GAME_MSG, 2, sizeof(Player_Disconnect_Message)),
                player(p.id) {}
        const Message_Logic& upcast() {return *(reinterpret_cast<const Message_Logic*>(this));}

        message_header header;
        uint8_t player; //The player's id
    };

    //****Entity gamestate update messages ****//
    struct Entity_Create_Message
    {
        Entity_Create_Message() : header(logic_thread_tid, GAME_MSG, ENTITY_CREATE, sizeof(Entity_Create_Message)) {}
        Entity_Create_Message(const game::Entity& e) :
                header(logic_thread_tid, GAME_MSG, ENTITY_CREATE, sizeof(Entity_Create_Message)),
                id(e.id), pos(e.pos), dir(e.dir) {strncpy(name, e.name, 64);}
        const Message_Logic& upcast() {return *(reinterpret_cast<const Message_Logic*>(this));}

        message_header header;
        uint32_t id;
        fixedpoint::vec2d pos, dir;
        uint32_t data_size;
        char name [64];
    };

    struct Entity_Update_Message
    {
        Entity_Update_Message() : header(logic_thread_tid, GAME_MSG, ENTITY_UPDATE, sizeof(Entity_Update_Message)) {}
        const Message_Logic& upcast() {return *(reinterpret_cast<const Message_Logic*>(this));}

        message_header header;
        uint32_t entity_id;
        uint16_t field;
        int64_t contents;
    };

    struct Entity_Destroy_Message
    {
        Entity_Destroy_Message() : header(logic_thread_tid, GAME_MSG, ENTITY_DESTROY, sizeof(Entity_Destroy_Message)) {}
        const Message_Logic& upcast() {return *(reinterpret_cast<const Message_Logic*>(this));}

        message_header header;
        uint32_t entity_id;
    };

    struct Player_Control_Message
    {
        Player_Control_Message() : header(logic_thread_tid, GAME_MSG, CONTROL_EVENT, sizeof(Player_Control_Message)) {}
        const Message_Logic& upcast() {return *(reinterpret_cast<const Message_Logic*>(this));}

        message_header header;
        uint8_t player_id;
        uint16_t control_id;
        struct analog_2d
        {
            int16_t x, y;
        };
        enum control_type {BINARY, ANALOG_2D, ANALOG_1D};
        uint8_t type;
        union
        {
            bool binary;
            analog_2d analog;
        } state;
    };

    struct Request_Join_Message
    {
        Request_Join_Message() : header(logic_thread_tid, GAME_MSG, REQUEST_JOIN, sizeof(Request_Join_Message)) {}
        Request_Join_Message(const char* name, uint8_t team = 0) : header(logic_thread_tid, GAME_MSG, REQUEST_JOIN, sizeof(Request_Join_Message)),
            desired_team(team) {strncpy(desired_name, name, 64);}
        const Message_Logic& upcast() {return *(reinterpret_cast<const Message_Logic*>(this));}

        message_header header;
        uint8_t desired_team;
        char desired_name [64];
    };

    struct Accept_Join_Message
    {
        Accept_Join_Message() : header(logic_thread_tid, GAME_MSG, ACCEPT_JOIN, sizeof(Accept_Join_Message)) {}
        Accept_Join_Message(uint8_t p) : header(logic_thread_tid, GAME_MSG, ACCEPT_JOIN, sizeof(Accept_Join_Message)),
            player_id(p) {}
        const Message_Logic& upcast() {return *(reinterpret_cast<const Message_Logic*>(this));}

        message_header header;
        uint8_t player_id;
    };

    struct Signal_Message
    {
        //Game-specific message
        //Interpreted almost entirely by lua, so this doesn't have much of a C specification
        Signal_Message() : header(logic_thread_tid, GAME_MSG, GAME_SIGNAL, sizeof(Signal_Message)) {}
        message_header header;
        //uint8_t type;
        enum{S_SERVER, S_CLIENT, S_HOST, S_ALL, S_ALL_CLIENTS}; //Different types of locality where this is to be processed
        uint8_t locality;
        uint16_t type; //Read by lua and it determines how to read the argument
        uint32_t argument;
    };

    struct Entity_Data_Update
    {
        Entity_Data_Update(uint32_t off, uint32_t s) : header(logic_thread_tid, GAME_MSG, DATA_UPDATE,
            sizeof(message_header) + 8 + s),
            offset(off) {}

        message_header header;

        uint32_t offset;
        uint32_t size;
        char buf [64]; //Can't we make this an arbitrary size somehow?
    };


    /*POD types are tough to derive for because we can't use C++ derivation
     * we really can't risk doing normal inheritance lest the compiler put
     * a fuckin' vtable in there.
     */

//    struct Message_Logic
//    {
//    public:
//        Message_Logic() : m.header(logic_thread_tid, GAME_MSG, 0xf, sizeof(message_header)) {}
//        //Copy constructor only copies as much as is necessary
//        Message_Logic(const Message_Logic& n)
//            {memcpy(this, &n, n.m.header.get_size());}
//        Message_Logic& operator= (const Message_Logic& n) {memcpy(this, &n, n.m.header.get_size()); return *this;}
//
//        uint8_t type() {return (m.header.type & 0xf);}
//
//        //This is mostly just to make sure that we have enough room for the largest type of actual message
//        union
//        {
//            message_header header;
//            Player_Connect_Message _pc;
//            Player_Update_Message _pu;
//            Player_Disconnect_Message _pd;
//            Entity_Create_Message _ec;
//            Entity_Update_Message _eu;
//            Entity_Destroy_Message _ed;
//
//        } m;
//    };
}

#endif // MESSAGE_LOGIC_H_INCLUDED
